package com.atguigu.gmall.list.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.list.repository.GoodsRepository;
import com.atguigu.gmall.list.service.SearchService;
import com.atguigu.gmall.model.list.*;
import com.atguigu.gmall.model.product.BaseAttrInfo;
import com.atguigu.gmall.model.product.BaseCategoryView;
import com.atguigu.gmall.model.product.BaseTrademark;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private GoodsRepository goodsRepository;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    //商品Goods上架功能
    @Override
    public void upperGoods(Long skuId) {
        Goods goods = new Goods();
        SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
        goods.setId(skuId);
        if (skuInfo != null){
            goods.setDefaultImg(skuInfo.getSkuDefaultImg());
            goods.setTitle(skuInfo.getSkuName());
            goods.setPrice(skuInfo.getPrice().doubleValue());
            goods.setCreateTime(new Date());

            BaseTrademark trademark = productFeignClient.getTrademark(skuInfo.getTmId());
            if (trademark != null){
                goods.setTmId(skuInfo.getTmId());
                goods.setTmName(trademark.getTmName());
                goods.setTmLogoUrl(trademark.getLogoUrl());
            }

            BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
            if (categoryView != null){
                goods.setCategory1Id(categoryView.getCategory1Id());
                goods.setCategory2Id(categoryView.getCategory2Id());
                goods.setCategory3Id(categoryView.getCategory3Id());
                goods.setCategory1Name(categoryView.getCategory1Name());
                goods.setCategory2Name(categoryView.getCategory2Name());
                goods.setCategory3Name(categoryView.getCategory3Name());
            }
        }
        List<BaseAttrInfo> attrList = productFeignClient.getAttrList(skuId);
        List<SearchAttr> searchAttrList = attrList.stream().map(baseAttrInfo -> {
            SearchAttr searchAttr = new SearchAttr();
            searchAttr.setAttrId(baseAttrInfo.getId());
            searchAttr.setAttrName(baseAttrInfo.getAttrName());
            searchAttr.setAttrValue(baseAttrInfo.getAttrValueList().get(0).getValueName());
            return searchAttr;
        }).collect(Collectors.toList());
        goods.setAttrs(searchAttrList);
        goodsRepository.save(goods);
    }

    //商品下架功能
    @Override
    public void lowerGoods(Long skuId) {
        goodsRepository.deleteById(skuId);
    }

    //商品热度排名
    @Override
    public void incrHotScore(Long skuId) {
        //利用redis中的ZSet
        String hotScoreKey = "hotScore";
        //将redis中ZSet型的数据（hotScore的集合中的（skuid:skuId数据）评分+1，并且返回评分）
        Double count = redisTemplate.opsForZSet().incrementScore(hotScoreKey, "skuid:" + skuId, 1);
        if (count % 10 == 0){
            Optional<Goods> optional = goodsRepository.findById(skuId);
            Goods goods = optional.get();
            if (goods == null){
                return;
            }
            goods.setHotScore(count.longValue());
            goodsRepository.save(goods);
        }
    }

    //搜索商品列表
    @Override
    public SearchResponseVo search(SearchParam searchParam) {
        //利用封装的方法，按照传来的参数searchParam，转成Es查询请求对象
        SearchRequest searchRequest = this.buildQueryDsl(searchParam);
        //调用Es高级客户端，发送请求，获取返回信息
        SearchResponse response = null;
        try {
            response= restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //调用自己封装方法，封装结果
        SearchResponseVo responseVo = this.parseSearchResult(response);

        responseVo.setPageNo(searchParam.getPageNo());
        responseVo.setPageSize(searchParam.getPageSize());
        long totalPages = (responseVo.getTotal() + responseVo.getPageSize() - 1) / responseVo.getPageSize();
        responseVo.setTotalPages(totalPages);

        return responseVo;
    }

    //按照传来的参数searchParam，转成Es查询请求对象
    private SearchRequest buildQueryDsl(SearchParam searchParam) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();//对应es查询dsl语句中的大括号
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); //布尔查询、过滤query---bool
        if(!StringUtils.isEmpty(searchParam.getCategory1Id())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("category1Id",searchParam.getCategory1Id()));
        }
        if(!StringUtils.isEmpty(searchParam.getCategory2Id())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("category2Id",searchParam.getCategory2Id()));
        }
        if(!StringUtils.isEmpty(searchParam.getCategory3Id())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("category3Id",searchParam.getCategory3Id()));
        }
        //标题关键词查询
        if (!StringUtils.isEmpty(searchParam.getKeyword())){
            boolQueryBuilder.must(QueryBuilders.matchQuery("title",searchParam.getKeyword()).operator(Operator.AND));
        }
        //品牌查询，前端传来的品牌为 4:小米 这样的类型
        String trademark = searchParam.getTrademark();
        if (!StringUtils.isEmpty(trademark)){
            String[] split = trademark.split(":");
            if (split != null && split.length == 2){
                boolQueryBuilder.filter(QueryBuilders.termQuery("tmId",split[0]));
            }
        }
        //平台属性/值，查询，传递参数 (23:8G:运行内存) (*:*:颜色) 字符串数组
        String[] props = searchParam.getProps();
        if (!StringUtils.isEmpty(props)){
            for (String prop : props) {
                String[] split = prop.split(":");
                if (split != null && split.length == 3){
                    BoolQueryBuilder level2BoolQuery1 = QueryBuilders.boolQuery();
                    BoolQueryBuilder level3BoolQuery1 = QueryBuilders.boolQuery();
                    level3BoolQuery1.must(QueryBuilders.termQuery("attrs.attrId",split[0]));
                    level3BoolQuery1.must(QueryBuilders.termQuery("attrs.attrValue", split[1]));
                    level2BoolQuery1.must(QueryBuilders.nestedQuery("attrs",level3BoolQuery1, ScoreMode.None));
                    boolQueryBuilder.filter(level2BoolQuery1);
                }
            }
        }
        searchSourceBuilder.query(boolQueryBuilder);
        //分页
        int from = (searchParam.getPageNo() - 1) * searchParam.getPageSize();
        searchSourceBuilder.from(from);
        searchSourceBuilder.size(searchParam.getPageSize());
        //排序传来的参数为order 1:desc/asc 代表按热度排序降序/升序， 2:desc/asc 代表按价格排序降序/升序
        String order = searchParam.getOrder();
        //给定默认排序规则（按hotScore排序）
        String field = "hotScore";
        String upDown = "desc";
        if (!StringUtils.isEmpty(order)){
            String[] split = order.split(":");
            if (split != null && split.length == 2){
                switch(split[0]){
                    case "1":
                        break;
                    case "2":
                        field = "price";
                        break;
                }
                upDown = split[1];
            }
        }
        searchSourceBuilder.sort(field,"asc".equals(upDown)? SortOrder.ASC:SortOrder.DESC);
        //高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();

        //HighlightBuilder highlightBuilder = searchSourceBuilder.highlight();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<span style=color:red>");
        highlightBuilder.postTags("</span>");

        searchSourceBuilder.highlighter(highlightBuilder);
        //聚合
        //品牌聚合
        TermsAggregationBuilder tmAgg = AggregationBuilders.terms("tmIdAgg").field("tmId")
                .subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName"))
                .subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl"));
        searchSourceBuilder.aggregation(tmAgg);
        //平台属性聚合
        NestedAggregationBuilder attrAgg = AggregationBuilders.nested("attrAgg", "attrs")
                .subAggregation(AggregationBuilders.terms("attrIdAgg").field("attrs.attrId")
                        .subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName"))
                        .subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue")));
        searchSourceBuilder.aggregation(attrAgg);

        //结果过滤，只查那些字段
        searchSourceBuilder.fetchSource(new String[]{"id","defaultImg","title","price"},null);

        //es请求对象，指定索引库“goods”
        SearchRequest searchRequest = new SearchRequest("goods");
        searchRequest.types("info");
        searchRequest.source(searchSourceBuilder);
        //输出显示一下
        System.out.println("Dsl"+searchSourceBuilder);
        return searchRequest;
    }

    //将查询结果封装进返回前端的对象中
    private SearchResponseVo parseSearchResult(SearchResponse response) {
        if (response == null){
            return new SearchResponseVo();
        }
        SearchResponseVo responseVo = new SearchResponseVo();
        //获取hits数据
        SearchHits hits = response.getHits();
        //总数量
        responseVo.setTotal(hits.totalHits);
        //hits中的二级hits 命中数据
        SearchHit[] subHits = hits.getHits();
        List<Goods> goodsList = new ArrayList<>();
        //封装List<Goods>数据
        if (subHits != null && subHits.length > 0){
            for (SearchHit subHit : subHits) {
                String sourceAsString = subHit.getSourceAsString();
                Goods goods = JSON.parseObject(sourceAsString,Goods.class);
                //判断有无高亮标题
                HighlightField highlightField = subHit.getHighlightFields().get("title");
                if (highlightField != null){
                    Text[] titles = highlightField.getFragments();
                    //放入高亮标题
                    goods.setTitle(titles[0].toString());
                }
                goodsList.add(goods);
            }
        }
        responseVo.setGoodsList(goodsList);
        //获取聚合信息
        Map<String, Aggregation> aggregationMap = response.getAggregations().asMap();

        //封装品牌信息
        ParsedLongTerms tmIdAgg = (ParsedLongTerms) aggregationMap.get("tmIdAgg");
        List<SearchResponseTmVo> trademarkList = tmIdAgg.getBuckets().stream().map(bucket -> {
            SearchResponseTmVo trademark = new SearchResponseTmVo();
            //设置id
            trademark.setTmId(Long.parseLong(((Terms.Bucket) bucket).getKeyAsString()));
            //获取id下聚合信息
            Map<String, Aggregation> tmIdSubAgg = ((Terms.Bucket) bucket).getAggregations().asMap();
            //设置品牌名
            ParsedStringTerms tmNameAgg = (ParsedStringTerms) tmIdSubAgg.get("tmNameAgg");
            trademark.setTmName(tmNameAgg.getBuckets().get(0).getKeyAsString());
            //设置品牌url
            ParsedStringTerms tmLogoUrlAgg = (ParsedStringTerms) tmIdSubAgg.get("tmLogoUrlAgg");
            trademark.setTmLogoUrl(tmLogoUrlAgg.getBuckets().get(0).getKeyAsString());
            return trademark;
        }).collect(Collectors.toList());
        responseVo.setTrademarkList(trademarkList);

        //封装平台属性信息
        ParsedNested attrAgg = (ParsedNested) aggregationMap.get("attrAgg");
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");
        //获取平台属性list
        List<SearchResponseAttrVo> attrList = attrIdAgg.getBuckets().stream().map(bucket -> {
            SearchResponseAttrVo attrVo = new SearchResponseAttrVo();
            //设置属性id
            attrVo.setAttrId(((Terms.Bucket) bucket).getKeyAsNumber().longValue());

            //设置属性名
            ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
            attrVo.setAttrName(attrNameAgg.getBuckets().get(0).getKeyAsString());
            //设置属性值list
            ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attrValueAgg");
            List<String> valueList = attrValueAgg.getBuckets().stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList());
            attrVo.setAttrValueList(valueList);
            return attrVo;
        }).collect(Collectors.toList());
        //平台属性值list放入对象
        responseVo.setAttrsList(attrList);

        return responseVo;
    }


}
